// Copyright (C) 2005-2008 Jae Stutzman (jaebird@gmail.com)
//
// This file is part of CastGen.
//
// CastGen is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// CastGen is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with CastGen.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.IO;
using System.Timers;
using ID3Lib;

namespace CastGen
{
	public class RSS2Generator
	{
		private const string Title = "TIT2";
		private const string Artist = "TPE1";
		private const string Album = "TALB";
		private const string Comment = "COMM";
		private const string Date = "TDAT";
		private const string Year = "TYER";
		private const string Time = "TIME";

		private readonly Timer generateTimeout = new Timer();
		FileSystemWatcher fsw = new FileSystemWatcher();

		string podcastRssDirectory;
		string podcastUrl;
		string podcastRssFile;
		int podcastItemCount;

		string mediaDirectory;
		string mediaUrl;

		string audioDirectoryName;
		string audioFileExtension;

		string notesDirectoryName;
		string notesExtension;

		string podcastXmlNamespace = string.Empty;
		string podcastXmlDtd = string.Empty;

		string channelTitle = string.Empty;
		string channelDescription = string.Empty;
		string channelCopyright = string.Empty;
		string channelWebmaster = string.Empty;

		public RSS2Generator()
		{
			podcastRssDirectory = System.Configuration.ConfigurationSettings.AppSettings.Get("PodcastRssDirectory");
			podcastUrl = System.Configuration.ConfigurationSettings.AppSettings.Get("PodcastUrl");
			podcastRssFile = System.Configuration.ConfigurationSettings.AppSettings.Get("PodcastRssFile");
			podcastItemCount = Int32.Parse(System.Configuration.ConfigurationSettings.AppSettings.Get("PodcastItemCount"));

			podcastXmlNamespace = System.Configuration.ConfigurationSettings.AppSettings.Get("PodcastXmlNamespace");
			podcastXmlDtd = System.Configuration.ConfigurationSettings.AppSettings.Get("PodcastXmlDtd");

			mediaDirectory = System.Configuration.ConfigurationSettings.AppSettings.Get("MediaDirectory");
			mediaUrl = System.Configuration.ConfigurationSettings.AppSettings.Get("MediaUrl");

			audioDirectoryName = System.Configuration.ConfigurationSettings.AppSettings.Get("AudioDirectoryName");
			audioFileExtension = System.Configuration.ConfigurationSettings.AppSettings.Get("AudioFileExtension");

			notesDirectoryName = System.Configuration.ConfigurationSettings.AppSettings.Get("NotesDirectoryName");
			notesExtension = System.Configuration.ConfigurationSettings.AppSettings.Get("NotesFileExtension");

			channelTitle = System.Configuration.ConfigurationSettings.AppSettings.Get("ChannelTitle");
			channelDescription = System.Configuration.ConfigurationSettings.AppSettings.Get("ChannelDescription");
			channelCopyright = System.Configuration.ConfigurationSettings.AppSettings.Get("ChannelCopyright");
			channelWebmaster = System.Configuration.ConfigurationSettings.AppSettings.Get("ChannelWebmaster");
		}

		public void Start()
		{
			if (!Directory.Exists(mediaDirectory))
			{
				Console.WriteLine("{0} directory does not exist.", mediaDirectory);
				Environment.Exit(0);
				return;
			}
			
			GenerateNewRSS();

			fsw.IncludeSubdirectories = true;
			fsw.Path = mediaDirectory;
			fsw.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName;
			fsw.Changed += new FileSystemEventHandler(OnChanged);
			fsw.Created += new FileSystemEventHandler(OnChanged);
			fsw.Deleted += new FileSystemEventHandler(OnChanged);
			fsw.Renamed += new RenamedEventHandler(OnRenamed);
			fsw.EnableRaisingEvents = true;

			generateTimeout.Interval = 1000;
			generateTimeout.AutoReset = false;
			generateTimeout.Elapsed += OnTimeout;
		}

		public void Stop()
		{
			fsw.Dispose();
		}

		public void OnChanged(object _o, FileSystemEventArgs fileSystemEventArgs)
		{
			HandleFileSystemEvent(fileSystemEventArgs.Name);
		}

		public void OnRenamed(object _o, RenamedEventArgs renamedEventArgs)
		{
			HandleFileSystemEvent(renamedEventArgs.Name);
		}

		private void HandleFileSystemEvent(string objectChanged)
		{
			if (objectChanged.IndexOf(podcastRssFile) < 0)
			{
				lock(this)
				{
					Console.WriteLine(objectChanged);
					generateTimeout.Stop();
					generateTimeout.Start();
				}
			}
		}

		private void OnTimeout(object sender, ElapsedEventArgs e)
		{
			GenerateNewRSS();
		}

		private void GenerateNewRSS()
		{
			lock (this)
			{
				if (!Directory.Exists(podcastRssDirectory))
				{
					Directory.CreateDirectory(podcastRssDirectory);
				}
				
				// create a RSS feed generator for the output
				//FileStream stream = new FileStream(Path.Combine(path, rssXmlFile), FileMode.Create, FileAccess.ReadWrite);
				StreamWriter sr = File.CreateText(Path.Combine(podcastRssDirectory, podcastRssFile));
				RSSFeedGenerator gen = new RSSFeedGenerator(sr);
				gen.WriteStartDocument(podcastXmlNamespace, podcastXmlDtd);
				gen.WriteStartChannel(channelTitle, podcastUrl, channelDescription, channelCopyright,
					channelWebmaster);

				string[] files = GetFilesForPodcast();
				//string[] files = Directory.GetFiles(mediaDirectory, "*" + audioFileExtension, SearchOption.AllDirectories);

				for (int index = 0; index < files.Length; index++)
				{
					string filename = files[index];
					//Console.WriteLine("File: {0}, Index: {1}", filename, index);
					try
					{
						string artist = string.Empty;
						string title = string.Empty;
						string album = string.Empty;
						string comment = string.Empty;
						DateTime publishedDate = DateTime.UtcNow;

						GetInfo(filename, ref artist, ref title, ref album, ref comment, ref publishedDate);

						FileInfo info = new FileInfo(filename);

						string adjustedMediaUrl = UrlCombine(mediaUrl, info.Directory.Parent.Name);
						string audioMediaUrl = UrlCombine(UrlCombine(adjustedMediaUrl, audioDirectoryName), info.Name);

						// find if there is a notes link
						string link = string.Empty;
						string notesFile = Path.GetFileNameWithoutExtension(info.Name) + notesExtension;
						string notesFilePath = Path.Combine(info.Directory.Parent.FullName, notesDirectoryName);
						if (File.Exists(Path.Combine(notesFilePath, notesFile)))
						{
							link = UrlCombine(UrlCombine(adjustedMediaUrl, notesDirectoryName), notesFile);
						}

						// generate the items here
						gen.WriteItem(title, link, comment, artist, publishedDate, album,
							audioMediaUrl, info.Length.ToString());
					}
					catch (Exception e)
					{
						Console.WriteLine(filename);						
						Console.WriteLine(e);
					}

				}

				// clear up
				gen.WriteEndChannel();
				gen.WriteEndDocument();
				gen.Close();
				
				Console.WriteLine("Updated RSS Feed");
			}
		}

		private string[] GetFilesForPodcast()
		{
			string[] files = Directory.GetFiles(mediaDirectory, "*" + audioFileExtension, SearchOption.AllDirectories);

			// sort into ordered set
			Array.Sort(files);

			int numberOfPodcasts = Math.Min(files.Length, podcastItemCount);

			string[] results = new string[numberOfPodcasts];

			// loop backwords so latest podcast is on the top...
			for (int index = files.Length - 1, newIndex = 0; newIndex < numberOfPodcasts; index--, newIndex++)
			{
				results[newIndex] = files[index];
			}

			return results;
		}

		private void GetInfo(string filename, ref string artist, ref string title,
			ref string album, ref string comment, ref DateTime publishedDate)
		{
			int min = 0;
			int hour = 0;
			int day = 0;
			int month = 0;
			int year = 0;

			MP3File file = new MP3File();
			TagModel tagModel = file.Read(filename);

			foreach (FrameBase frameBase in tagModel.Frames)
			{
				switch (frameBase.Tag)
				{
					case Title:
					{
						title = frameBase.ToString();
						break;
					}
					case Artist:
					{
						artist = frameBase.ToString();
						break;
					}
					case Album:
					{
						album = frameBase.ToString();
						break;
					}
					case Time:
					{
						string time = frameBase.ToString();
						hour = Convert.ToInt32(time.Substring(0, 2));
						min = Convert.ToInt32(time.Substring(2, 2));
						break;
					}
					case Date:
					{
						string date = frameBase.ToString();
						day = Convert.ToInt32(date.Substring(0, 2));
						month = Convert.ToInt32(date.Substring(2, 2));
						break;
					}
					case Year:
					{
						year = Convert.ToInt32(frameBase.ToString());
						break;
					}
					case "TCOM":
					{
						break;
					}
					case "TCON":
					{
						// content type (genere)
						break;
					}
					case "TRCK":
					{
						// track number
						break;
					}
					case "USLT":
					{
						// lyrics
						break;
					}
					case Comment:
					{
						FrameLCText lcFrame = (FrameLCText)(frameBase);
						comment = lcFrame.Text;
						//TODO: add --> lcFrame.Language;
						break;
					}
					case "APIC":
					{
						// picture
						break;
					}
				}
			}

			if (year > 0 && month > 0 && day > 0)
			{
				publishedDate = new DateTime(year, month, day, hour, min, 0, DateTimeKind.Utc);
				//publishDate = dateTime.ToString("r");
			}
		}

		private string UrlCombine(string urlPath1, string urlPath2)
		{
			return urlPath1.TrimEnd('/') + "/" + urlPath2.TrimStart('/');
		}
	}
}
